﻿#region Using

using System.Collections.Generic;
using System.Linq;

using Common.Base;

using CommunicationLibrary.TransportData;

using GameLogic.External;

using ServerApplication.Interfaces;
using ServerApplication.Networking;

#endregion

namespace ServerApplication.GameSession
{
    public class GameSessionManager : ICoreToGameSessionManager, IReadySessionManager, IGameSessionManager
    {
        private readonly IGameSessionManagerToCore core;

        private readonly List<GameSession> gameSessions;

        private readonly List<PreSession> preSessions;

        private readonly IGameWorker worker;

        public GameSessionManager(IGameSessionManagerToCore core)
        {
            this.core = core;
            this.preSessions = new List<PreSession>();
            this.gameSessions = new List<GameSession>();
            this.worker = new GameWorker();
        }

        public void CreateGameSession(IEnumerable<ServerPlayer> players)
        {
            var serverPlayers = players as IList<ServerPlayer> ?? players.ToList();
            var preSession = new PreSession(serverPlayers, this);
            lock (preSession)
            {
                this.preSessions.Add(preSession);
            }
            this.core.SendGameReady(preSession.StartingState, serverPlayers);
        }

        public Result Execute(ICommand cmd)
        {
            lock (this.gameSessions)
            {
                foreach (var gameSession in this.gameSessions)
                {
                    if (gameSession.Contains(cmd.Player))
                    {
                        return gameSession.Execute(cmd);
                    }
                }

                return Result.Failure("You are not playing!");
            }
        }

        public void HandleGameOver(IGame game, Identifiable winner, GameSession sender)
        {
            lock (this.gameSessions)
            {
                this.gameSessions.Remove(sender);
            }

            this.worker.Deregister(game);
            this.core.SendPlayerWon(winner);
        }

        public void HandleGamePaused(List<ServerPlayer> players)
        {
            this.core.SendGamePaused(players);
        }

        public void HandleGameResumed(List<ServerPlayer> players)
        {
            this.core.SendGameResumed(players);
        }

        public Result PauseGame(ServerPlayer player)
        {
            lock (this.gameSessions)
            {
                foreach (var gameSession in this.gameSessions)
                {
                    if (gameSession.Contains(player))
                    {
                        return gameSession.Pause(player);
                    }
                }

                return Result.Failure("You are not playing!");
            }
        }

        public void PublishGameState(GameState gameState, IEnumerable<ServerPlayer> players)
        {
            this.core.SendGameState(gameState, players);
        }

        public void PublishPlayerLoose(Identifiable looser, IEnumerable<Identifiable> receivers)
        {
            this.core.SendPlayerLost(looser, receivers);
        }

        public Result QuitGame(ServerPlayer player)
        {
            lock (this.gameSessions)
            {
                foreach (var gameSession in this.gameSessions)
                {
                    if (gameSession.Contains(player))
                    {
                        gameSession.Quit(player);
                        return Result.Success;
                    }
                }

                return Result.Failure("You are not playing!");
            }
        }

        public Result ResumeGame(ServerPlayer player)
        {
            lock (this.gameSessions)
            {
                foreach (var gameSession in this.gameSessions)
                {
                    if (gameSession.Contains(player))
                    {
                        return gameSession.Resume(player);
                    }
                }

                return Result.Failure("You are not playing!");
            }
        }

        public void StartGame(IGame game, IEnumerable<ServerPlayer> players, PreSession sender)
        {
            lock (this.preSessions)
            {
                this.preSessions.Remove(sender);
            }

            var serverPlayers = players as IList<ServerPlayer> ?? players.ToList();
            lock (this.gameSessions)
            {
                this.gameSessions.Add(new GameSession(game, serverPlayers, this));
            }

            this.worker.Register(game);
            this.core.SendGameStarted(serverPlayers);
            game.Start();
        }

        public bool TryMarkReady(ServerPlayer player)
        {
            lock (this.preSessions)
            {
                foreach (var preSession in this.preSessions)
                {
                    if (preSession.Contains(player))
                    {
                        preSession.MarkReady(player);
                        return true;
                    }
                }

                return false;
            }
        }
    }
}